package edu.caece.langprocessor.syntax.parse;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import edu.caece.langprocessor.lexical.LexicalProcessor;
import edu.caece.langprocessor.syntax.GrammarItem;
import edu.caece.langprocessor.syntax.Production;
import edu.caece.langprocessor.syntax.ProductionFactory;
import edu.caece.langprocessor.syntax.Terminal;
import edu.caece.langprocessor.syntax.tree.Tree;
import edu.caece.langprocessor.syntax.tree.nodes.SimpleTreeNode;

public class ReduceAction extends Action {

	private static final Log LOGGER = LogFactory.getLog(ReduceAction.class);

	
	public ReduceAction(Integer value) {
		this.type = ActionType.reduce;
		this.value = value;
	}

	@Override
	public ActionResult run(LexicalProcessor lexicalProcessor,
			Map<Integer, ProductionFactory> productions, 
			GotoActionsTable gotoActionsTable,
			Stack<ParseStackItem> parseStack, Terminal terminalItem, 
			Tree parseTree) {
		
		Stack<GrammarItem> treeStack = new Stack<GrammarItem>();

		// aplicamos la reduccion
		Production p = productions.get(value).newInstance();
		List<GrammarItem> rightSideItems = p.getRightSideItems(); 

		//desapilamos
		for (int i = rightSideItems.size(); i> 0 ; i--) {
			ParseStackItem pi = parseStack.pop();
			if(!pi.getItem().equals(rightSideItems.get(i-1))){
				LOGGER.warn("Stack item differs from production.");
			}
			//agregamos en la pila local
			treeStack.add(pi.getItem());
		}
		
		//TODO resolver el tipado de nodos!
		//Ahora armamos el nodo del arbol
		while (!treeStack.empty()){
			parseTree.addChildByNodeType(p.getProductionClass() ,p.getLeftSideItem(), treeStack.pop());
		}
			
		//preparo el nuevo item a apilar
		Integer state = parseStack.peek().getState();
		Integer newState = gotoActionsTable
				.getState(state, p.getLeftSideItem());
		ParseStackItem psi = new ParseStackItem(p.getLeftSideItem(), newState);
		parseStack.push(psi);

		if (LOGGER.isDebugEnabled())
			LOGGER.debug("Reduce applied. Rule " + p.toString() + " reduced. New state: " + newState);
		
		return new ActionResult(terminalItem, new SimpleTreeNode(p.getLeftSideItem()), 0, new ArrayList<Terminal>());
	}

}
